#include "loaddbc.h"
#include <QDebug>
#include <QFile>
#include <QRegExp>      // 正则表达式
#include <QMessageBox>
/*
bool loadDbcClass::loadVersion(dbcClass &dbc, QFile &file, qint64 &fileLineNo)
{
    // VERSION 的正则表达式
    // 匹配的第1个字符串是VERSION,规则是：（从开头起（^），空格出现0次或多次，然后是VERSION）；  ^( {0,}VERSION)
    // 匹配的第2个字符串是前半个引号，规则是：紧跟VERSION是1个空格， 找半个分号， 其中多外\是转意；（空格）(\\\")
    // 匹配的第3个字符串是引号之间的内容，可能为空，也可能有多个字符； (.*)
    // 匹配的第3个字符串是之半个引号；(\\\")
     QRegExp versionRegExp("^( {0,}VERSION) (\\\")(.*)(\\\")");
     QString line;      // 用来存放每次读出的一行
     QStringList res;   // 用来存放匹配的结果
     int pos;        // 接收正则匹配位置
     QString qmsg;
     if(!file.seek(0))  // 光标切到文件最开始
     {
         qDebug() << "seek error " << endl;
         return false;
     }
     fileLineNo = 0;

     do  // 略过前面的空行
     {
         line = file.readLine();
         fileLineNo++;
     }while((line == "\r\n") && (!file.atEnd()));
     qDebug() << "version line" << fileLineNo << endl;

     pos  = versionRegExp.indexIn(line);
     if(-1 == pos)
     {
         qDebug() << "version no match" << endl;
         qDebug() << line;
         qmsg = "line ";
         qmsg.append(QString::number(fileLineNo));
         qmsg.append(": dbc VERSION error!  ");
         QMessageBox::information(nullptr, "ERROR", qmsg);
         return false;
     }
     else
     {
         res = versionRegExp.capturedTexts();
         dbc.version = res[3];
         qDebug() << "dbc.version:" << dbc.version << endl;
         qDebug() << "匹配成功" << endl;
         qDebug() << res;
     }
     return true;
}

*/

bool loadDbcClass::loadMsg(QString &line,dbcClass &dbc,qint64 lineNo)
{
    // msg 的正则表达式
    // ^( {0,}BO_)  从开头开始，空格0次或多次，然后是BO_;
    // (空格)(\\d+)      1个空格，然后是数字1次或多次
    // (空格)([^ ]+):    1个空格，然后是非空格1次或多次，然后1个冒号
    // (空格)(\\d+)      1个空格，然后是数字1次或多次
    // (空格)([^\r\n]+)  1个空格，然后是非\r\n 出现1次或多次
    QRegExp mesRegExp("^( {0,}BO_)( +)(\\d+)( +)([^ ]+)( {0,}):( +)(\\d+)( +)([^\r\n]+)");

    // signal 的正则表达式  （暂不支持 mux功能）
    // ^( {0,}SG_)( +)     从头开始，空格出现0次或多次，然后是SG_,然后是空格出现1次或多次
    // ([^ ]+)( +):        非空格出现1次或多次，然后是空格出现1次或多次，然后是：
    // ( +)(\\d+)\\|(\\d+)@(\\d)(\\+|\\-)  空格出现1次或多次，数字1次或多次，|，数字1次或多次，@，数字1次，+或-1次
    // ( +)\\((.*),(.*)\\)     空格1次或多次，（，单个字符任意次，逗号，单个字符任意次，）
    // ( +)\\[(.*)\\|(.*)\\]   空格1次或多次，[,单个字符任意次，|，单个字符任意次，]
    // ( +)\\\"(.*)\\\"        空格1次或多次，",单个字符任意次，",
    // ( +)([^\r\n]+)          空格1次或多次，非\r\n的字符多次
    QRegExp sigRegExp("^( {0,}SG_)( +)([^ ]+)( +):( +)(\\d+)\\|(\\d+)@(\\d)(\\+|\\-)( +)\\((.*),(.*)\\)( +)\\[(.*)\\|(.*)\\]( +)\\\"(.*)\\\"( +)([^\r\n]+)");


    QStringList res;   // 用来存放匹配的结果
    DBcMessage_t msg;  // 用来存放当前解析出来的msg头
    DBcSignal_t sig;   // 用来存放当前解析出来的信号
    bool con_ok = false;
    int i;
    QString qmsg;
    if(-1 != mesRegExp.indexIn(line))     // 如果匹配到msg头
    {
        res = mesRegExp.capturedTexts();
        msg.header.id          = res[3].toUInt(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": can ID convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        msg.header.dlc         = res[8].toUInt(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": can DLC convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        msg.header.name        = res[5];
        msg.header.transmitter = res[10];
        qDebug() << "a new msg header";
        for(i=0; i<res.size(); i++)
        {
            qDebug() << i << res[i];
        }
        dbc.msgList.append(msg);
    }
    else if(-1 != sigRegExp.indexIn(line))
    {
        res = sigRegExp.capturedTexts();
        qDebug() << "a new sig";
        sig.startBit = res[6].toUInt(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal start bit convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        sig.bitSize  = res[7].toUInt(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal bit size convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        sig.byteOrder = res[8].toUInt(&con_ok)==0? bigEndian : littleEndia;
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal byte order convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        sig.valueType      = res[9] == "+" ? unsignedType : signedType;
        sig.exVauleType    = signedOrUnsigned;
        sig.mux            = normalSignal;
        sig.muxSwitchValue = 0;
        sig.factor         = res[11].toDouble(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal factor convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        sig.offset = res[12].toDouble(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal offset convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        sig.min = res[14].toDouble(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal min value convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        sig.max = res[15].toDouble(&con_ok);
        if(false == con_ok)
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal max value convertion error!  ");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
        sig.name = res[3];
        sig.unit = res[16];
        for(i=0; i<res.size(); i++)
        {
            qDebug() << i << res[i];
            if(i>=19)
            {
                sig.receiver << res[i];
            }
        }
        dbc.msgList.last().sigList.append(sig);
    }
    else
    {
        qDebug("msg loading finished ");
        return false;
    }


    return true;
}

bool loadDbcClass::loadCom(QString &line, dbcClass &dbc, qint64 lineNo)
{
    // 无指定对象的注释
    // ^( {0,}CM_)
    QRegExp nonComRegExp( "^( {0,}CM_)( +)\\\"(.*)\\\";(.*)");

    QRegExp nodeComRegExp("^( {0,}CM_)( +)(BU_)( +)(.*)( +)\\\"(.*)\\\";(.*)");
    QRegExp msgComRegExp( "^( {0,}CM_)( +)(BO_)( +)(\\d+)( +)\\\"(.*)\\\";(.*)");
    QRegExp sigComRegExp( "^( {0,}CM_)( +)(SG_)( +)(\\d+)( +)(.*)( +)\\\"(.*)\\\";(.*)");
    QRegExp envComRegExp( "^( {0,}CM_)( +)(EV_)( +)(.*)( +)\\\"(.*)\\\";(.*)");
    QStringList list = line.split(" ");
    int i;
    bool con_ok;
    QStringList res;   // 用来存放匹配的结果
    QString qmsg;
    DBcComNode_t  comNode;
    DBcComMsg_t   comMsg;
    DBcComSig_t   comSig;
    DBcComEV_t    comEv;
    DBcComNoObj_t comNon;
    qDebug() << "line" << lineNo << ": " << line;
    for(i=0; i<list.size(); i++)
    {
        qDebug() << i << list[i];
    }
    if(list[0] == "CM_")
    {
        if(list[1] == "BU_")
        {
            qDebug() << "line " << lineNo << " node comment";
            if(-1 == nodeComRegExp.indexIn(line))
            {
                qmsg = "line ";
                qmsg.append(QString::number(lineNo));
                qmsg.append(": node comment error!");
                QMessageBox::information(nullptr, "ERROR", qmsg);
                return false;
            }
             res = nodeComRegExp.capturedTexts();
             qDebug() << res;
             for(i=0; i<res.size(); i++)
             {
                 qDebug() << i << res[i];
             }
             comNode.nodeName = res[5];
             comNode.com      = res[7];
             dbc.comNode.append(comNode);
        }
        else if(list[1] == "BO_")
        {
            qDebug() << "line " << lineNo << " message comment";
            if(-1 == msgComRegExp.indexIn(line))
            {
                qmsg = "line ";
                qmsg.append(QString::number(lineNo));
                qmsg.append(": message comment error!");
                QMessageBox::information(nullptr, "ERROR", qmsg);
                return false;
            }
             res = msgComRegExp.capturedTexts();
             qDebug() << res;
             for(i=0; i<res.size(); i++)
             {
                 qDebug() << i << res[i];
             }
             comMsg.msgId = res[5].toUInt(&con_ok);
             if(false == con_ok)
             {
                 qmsg = "line ";
                 qmsg.append(QString::number(lineNo));
                 qmsg.append(": comment message ID convertion error!  ");
                 QMessageBox::information(nullptr, "ERROR", qmsg);
                 return false;
             }
             comMsg.com   = res[7];
             dbc.comMsg.append(comMsg);
        }
        else if(list[1] == "SG_")
        {
            qDebug() << "line " << lineNo << " message comment";
            if(-1 == sigComRegExp.indexIn(line))
            {
                qmsg = "line ";
                qmsg.append(QString::number(lineNo));
                qmsg.append(": signal comment error!");
                QMessageBox::information(nullptr, "ERROR", qmsg);
                return false;
            }
             res = sigComRegExp.capturedTexts();
             qDebug() << res;
             for(i=0; i<res.size(); i++)
             {
                 qDebug() << i << res[i];
             }
             comSig.msgId = res[5].toUInt(&con_ok);
             comSig.sigName = res[7];
             comSig.com     = res[9];
             dbc.comSig.append(comSig);
        }
        else if(list[1] == "EV_")
        {
            qDebug() << "line " << lineNo << " message comment";
            if(-1 == envComRegExp.indexIn(line))
            {
                qmsg = "line ";
                qmsg.append(QString::number(lineNo));
                qmsg.append(": environment value comment error!");
                QMessageBox::information(nullptr, "ERROR", qmsg);
                return false;
            }
             res = envComRegExp.capturedTexts();
             qDebug() << res;
             for(i=0; i<res.size(); i++)
             {
                 qDebug() << i << res[i];
             }
             comEv.evName = res[5];
             comEv.com    = res[7];
             dbc.comEv.append(comEv);
        }
        else
        {
            qDebug() << "line " << lineNo << " message comment";
            if(-1 == nonComRegExp.indexIn(line))
            {
                qmsg = "line ";
                qmsg.append(QString::number(lineNo));
                qmsg.append(": none obj comment error!");
                QMessageBox::information(nullptr, "ERROR", qmsg);
                return false;
            }
             res = nonComRegExp.capturedTexts();
             qDebug() << res;
             for(i=0; i<res.size(); i++)
             {
                 qDebug() << i << res[i];
             }
             comNon.com = res[3];
             dbc.comNoObj.append(comNon);
        }
    }
    else
    {
        return false;
    }


    return true;

}

bool loadDbcClass::loadSigExValueType(QString &line, dbcClass &dbc, qint64 lineNo)
{
    QRegExp exValueTypeRegExp( "^( {0,}SIG_VALTYPE_)( +)(\\d+)( +)([^ ]+)( {0,}):( {0,})(\\d)( {0,});(.*)");
    QStringList list = line.split(" ");
    QString qmsg;
    int i;
    SIgnalExValueType_t exT;
    QStringList res;
    bool con_ok = false;
    if(list[0] == "SIG_VALTYPE_")
    {
        qDebug() << "line " << lineNo << " ex value type";
        qDebug() << line;
        for(i=0; i<list.size(); i++)
        {
            qDebug() << i << list[i];
        }
        if(-1 == exValueTypeRegExp.indexIn(line))
        {
            qmsg = "line ";
            qmsg.append(QString::number(lineNo));
            qmsg.append(": signal extended value type error!");
            QMessageBox::information(nullptr, "ERROR", qmsg);
            return false;
        }
         res = exValueTypeRegExp.capturedTexts();
         qDebug() << res;
         for(i=0; i<res.size(); i++)
         {
             qDebug() << i << res[i];
         }
         exT.msgId = res[3].toUInt(&con_ok);
         if(false == con_ok)
         {
             qmsg = "line ";
             qmsg.append(QString::number(lineNo));
             qmsg.append(": signal extended value type : message ID convertion error!  ");
             QMessageBox::information(nullptr, "ERROR", qmsg);
             return false;
         }
         exT.signalName = res[5];
         if(res[8] == "0")
         {
             exT.ExType = signedOrUnsigned;
         }
         else if(res[8] == "1")
         {
             exT.ExType = IeeeFloat;
         }
         else if(res[8] == "2")
         {
             exT.ExType = IeeeDouble;
         }
         else
         {
             qmsg = "line ";
             qmsg.append(QString::number(lineNo));
             qmsg.append(": signal extended value type : type error!  ");
             QMessageBox::information(nullptr, "ERROR", qmsg);
             return false;
         }
         dbc.exTypeList.append(exT);
    }
    return true;
}

void loadDbcClass::updateExValType(dbcClass &dbc)
{
    int i,j,k;
    qDebug() << "extend value type num : " << dbc.exTypeList.size();
    for(i=0; i<dbc.exTypeList.size(); i++)
    {
        qDebug() << "loop exTypeList : " << i;
        for(j=0; j<dbc.msgList.size(); j++)
        {
             qDebug() << "loop msgList : " << j;
            if(dbc.msgList[j].header.id == dbc.exTypeList[i].msgId)
            {
                for(k=0; k<dbc.msgList[j].sigList.size(); k++)
                {
                    qDebug() << "loop sigList : " << k;
                    if(dbc.msgList[j].sigList[k].name == dbc.exTypeList[i].signalName)
                    {
                        dbc.msgList[j].sigList[k].exVauleType = dbc.exTypeList[i].ExType;
                        qDebug() << "update one extend value type : msg id = " <<  dbc.msgList[j].header.id
                                 << "signal name = " << dbc.msgList[j].sigList[k].name;
                    }
                }
            }

        }
    }
}

bool loadDbcClass::isEmptyLine(QString &line)
{
    QRegExp RegExp("^\\s*");
   // qDebug() << "before: " << line;
    line.replace(RegExp, "");
  //  qDebug() << "after: " << line;
    return line.isEmpty();
}

bool loadDbcClass::loadVersion(QString &line, dbcClass &dbc,qint64 lineNo)
{
    // VERSION 的正则表达式
    // 匹配的第1个字符串是VERSION,规则是：（从开头起（^），空格出现0次或多次，然后是VERSION）；  ^( {0,}VERSION)
    // 匹配的第2个字符串是前半个引号，规则是：紧跟VERSION是1个空格， 找半个分号， 其中多外\是转意；（空格）(\\\")
    // 匹配的第3个字符串是引号之间的内容，可能为空，也可能有多个字符； (.*)
    // 匹配的第3个字符串是之半个引号；(\\\")
     QRegExp versionRegExp("^( {0,}VERSION) (\\\")(.*)(\\\")");
     QStringList res;   // 用来存放匹配的结果
     QString qmsg;
     if(-1 == versionRegExp.indexIn(line))
     {
         qDebug() << "version not matched " << endl;
         qDebug() << line;
         qmsg = "line ";
         qmsg.append(QString::number(lineNo));
         qmsg.append(": dbc VERSION error!  ");
         QMessageBox::information(nullptr, "ERROR", qmsg);
         return false;
     }
     res = versionRegExp.capturedTexts();
     dbc.version = res[3];
     qDebug() << "[1]: ************************************************************";
     qDebug() << res;
     qDebug() << "dbc.version:" << dbc.version << endl;
     return true;
}

bool loadDbcClass::isNsStart(QString &line)
{
    // new symbol 的正则表达式
    // ^( {0,}NS_ {0,}:)  从头开始，0或多个空格 NS_,0或多个空格，1个冒号
    QRegExp newSymbolRegExp("^( {0,}NS_ {0,}:)");
    QStringList res;   // 用来存放匹配的结果
    QString qmsg;
    if(-1 == newSymbolRegExp.indexIn(line))
    {
        qDebug() << "new symbol not matched " << endl;
        qDebug() << line;
        qmsg = "line";
        qmsg.append(QString::number(fileLineCnt));
        qmsg.append(": dbc new symbol error!  ");
        QMessageBox::information(nullptr, "ERROR", qmsg);
        return false;
    }
    res = newSymbolRegExp.capturedTexts();
    qDebug() << "new symbol matched :" << res;
    return true;

}

bool loadDbcClass::isBsStart(QString &line)
{
    // BS 的正则表达式
    // ^( {0,}BS_:)  从头开始，0或多个空格，BS_:,然后任意字符任意次
    QRegExp bsRegExp("^( {0,}BS_:)(.*)");
    if(-1 == bsRegExp.indexIn(line)) {return false;}
    qDebug() << "BS start matched " << bsRegExp.capturedTexts();;
    return true;
}

void loadDbcClass::loadNewSymbol(QString &line, dbcClass &dbc)
{
     line.replace(" ","");
     line.replace("\t", "");
     line.replace("\r", "");
     line.replace("\n", "");
     dbc.newSymbol << line;
     qDebug() << "add a new symbol :" << line;
}

void loadDbcClass::loadBs(QString &line, dbcClass &dbc)
{
     dbc.bitTiming = line;
     qDebug() << "loaded Bs :" << line;
}

bool loadDbcClass::loadNode(QString &line, dbcClass &dbc)
{

    QRegExp nodeRegExp("^( {0,}BU_:)");    // 从开头起，0或多个空格， BU_:
    QString qmsg;
    if(-1 == nodeRegExp.indexIn(line))
    {
        qDebug() << "node not matched " << endl;
        qDebug() << line;
        qmsg = "line";
        qmsg.append(QString::number(fileLineCnt));
        qmsg.append(": node error!  ");
        return false;
    }
    line.replace("\r", "");
    line.replace("\n", "");
    QStringList list = line.split(" ");
    int i;
    qDebug() << "line before split:" << line;
    qDebug() << "size of list :" << list.size();
    for(i=0; i<list.size(); i++)
    {
        if((i>0) && (!list[i].isEmpty()))
        {
            dbc.node << list[i];
        }
        qDebug() << list[i];
    }
    qDebug() << "node matched " << endl;
    return true;
}

loadDbcClass::loadDbcClass()
{


}



void loadDbcClass::load(QString &dbcPath , dbcClass &dbc)
{
    if(dbcPath.isNull())   // 如果是空路径就直接return
    {
        qDebug() << "null path" << endl;
        return;
    }
    QFile file(dbcPath);
    if(!(file.open(QIODevice::ReadOnly)))  // 如果打开方件出错，就直接return
    {
       qDebug() << "open dbc file error"  << endl;
        QMessageBox::information(nullptr, "ERROR", "打开dbc文件失败");
       return;
    }
    qDebug() << "open dbc file success " << endl;

  //  loadVersion(dbc,file,fileLineNo);

    file.seek(0);     // 切换到头件最开始
    fileLineCnt = 0;  // 行号计数器清0
    QString line;
    QString qmsg;

/************************************************************
 * 【1】加载 VERSION
*************************************************************/
    while(!file.atEnd())
    {
        line = file.readLine();                       // 读1行
        fileLineCnt++;                                // 行号计数器加1
        if(isEmptyLine(line))      {  continue; }     // 跳过空行
        if(loadVersion(line,dbc,fileLineCnt))  {  break;    }     // 加载VERSIONG
        else {return ;}                               // 如果加载VERSION出错，直接返回
    }
/************************************************************
 * 【2】加载 new symbol
*************************************************************/
// 找new bymbol的开始
    while(!file.atEnd())
    {
        line = file.readLine();                   // 读1行
        fileLineCnt++;                            // 行号计数器加1
        if(isEmptyLine(line))  {  continue; }     // 跳过空行
        if(!isNsStart(line))   {return;}          // 如果空行跳完还不是new symbol，直接返回
        else                   {break;}           // 如果是正确的new symbol 开头，跳过此行，从下1行开始是new symbol的内容

    }
// 加载 new symbol 的内容
    while(!file.atEnd())
    {
        line = file.readLine();                       // 读1行
        fileLineCnt++;                                // 行号计数器加1
        if(isEmptyLine(line))      {  continue; }     // 跳过空行
        if(isBsStart(line))      // 如果向下一直找到了BS_ 表示 NS部分已加载完毕
        {

            break;
        }
        else
        {
            loadNewSymbol(line,dbc);
        }
    }

/************************************************************
 * 【3】加载 BS_
*************************************************************/
    loadBs(line,dbc);

/************************************************************
 * 【4】加载 node
*************************************************************/
    while(!file.atEnd())
    {
        line = file.readLine();                       // 读1行
        fileLineCnt++;                                // 行号计数器加1
        if(isEmptyLine(line))      {  continue; }     // 跳过空行
        if(!loadNode(line, dbc))                      // 如果没有找到node直接return
        {
            return;
        }
        else                                          // 如果找到了node则跳过，进行下一步
        {
            break;
        }
    }
/************************************************************
 * 【4】加载报文和信号
*************************************************************/

    while(!file.atEnd())
    {
        line = file.readLine();                       // 读1行
        fileLineCnt++;                                // 行号计数器加1
        if(isEmptyLine(line))      {  continue; }     // 跳过空行
        if(!loadMsg(line,dbc,fileLineCnt))
        {
            break;
        }
    }

/************************************************************
 * 【5】加载注释
*************************************************************/

 // 上一步已读出一行注释
     loadCom(line,dbc,fileLineCnt);
     while(!file.atEnd())
     {
         line = file.readLine();                       // 读1行
         fileLineCnt++;                                // 行号计数器加1
         if(isEmptyLine(line))      {  continue; }     // 跳过空行
         if(!loadCom(line,dbc,fileLineCnt))
         {
             break;
         }
     }
 /************************************************************
  * 【6】加载信号扩展数据类型
 *************************************************************/
     loadSigExValueType(line,dbc,fileLineCnt);
     while(!file.atEnd())
     {
         line = file.readLine();                       // 读1行
         fileLineCnt++;                                // 行号计数器加1
         if(isEmptyLine(line))      {  continue; }     // 跳过空行
         loadSigExValueType(line,dbc,fileLineCnt);
//         if(!loadSigExValueType(line,dbc,fileLineCnt))
//         {
//             break;
//         }
     }
     updateExValType(dbc);
     file.close();
}
